﻿using FastApi.Attributes;
using System.Diagnostics;
using System.Globalization;
using System.Text;

namespace FastApi
{
    public class FastClientSdk
    {

        #region static

        private static async Task<string> RunDotnetCmdAsync(string cmd, string workDir)
        {
            ProcessStartInfo startInfo = new()
            {
                FileName = "dotnet",
                Arguments = cmd,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                CreateNoWindow = true,
                WorkingDirectory = workDir,
                RedirectStandardInput = true,
                StandardOutputEncoding = Encoding.UTF8,
                RedirectStandardError = true,
                StandardErrorEncoding = Encoding.UTF8
            };

            using var process = Process.Start(startInfo);

            using var reader = process.StandardOutput;
            string result = await reader.ReadToEndAsync();

            using var reader2 = process.StandardError;
            result += await reader2.ReadToEndAsync();

            return result;
        }

        private static string GetSimpleTypeName(Type type)
        {
            if (type == typeof(string))
            {
                return "string";
            }
            if (type == typeof(char))
            {
                return "char";
            }
            if (type == typeof(char?))
            {
                return "char?";
            }


            if (type == typeof(int))
            {
                return "int";
            }
            if (type == typeof(int?))
            {
                return "int?";
            }
            if (type == typeof(uint))
            {
                return "uint";
            }
            if (type == typeof(uint?))
            {
                return "uint?";
            }


            if (type == typeof(long))
            {
                return "long";
            }
            if (type == typeof(long?))
            {
                return "long?";
            }
            if (type == typeof(ulong))
            {
                return "ulong";
            }
            if (type == typeof(ulong?))
            {
                return "ulong?";
            }

            if (type == typeof(DateTime))
            {
                return "DateTime";
            }
            if (type == typeof(DateTime?))
            {
                return "DateTime?";
            }

            if (type == typeof(bool))
            {
                return "bool";
            }
            if (type == typeof(bool?))
            {
                return "bool?";
            }

            if (type == typeof(decimal))
            {
                return "decimal";
            }
            if (type == typeof(decimal?))
            {
                return "decimal?";
            }

            if (type == typeof(double))
            {
                return "double";
            }
            if (type == typeof(double?))
            {
                return "double?";
            }

            if (type == typeof(float))
            {
                return "float";
            }
            if (type == typeof(float?))
            {
                return "float?";
            }

            if (type == typeof(DateTimeOffset))
            {
                return "DateTimeOffset";
            }
            if (type == typeof(DateTimeOffset?))
            {
                return "DateTimeOffset?";
            }

            if (type == typeof(short))
            {
                return "short";
            }
            if (type == typeof(short?))
            {
                return "short?";
            }
            if (type == typeof(ushort))
            {
                return "ushort";
            }
            if (type == typeof(ushort?))
            {
                return "ushort?";
            }

            if (type == typeof(byte))
            {
                return "byte";
            }
            if (type == typeof(byte?))
            {
                return "byte?";
            }
            if (type == typeof(sbyte))
            {
                return "sbyte";
            }
            if (type == typeof(sbyte?))
            {
                return "sbyte?";
            }


            if (type == typeof(Enum))
            {
                return "int";
            }


            if (type == typeof(Guid))
            {
                return "Guid";
            }
            if (type == typeof(Guid?))
            {
                return "Guid?";
            }

            if (type == typeof(TimeSpan))
            {
                return "TimeSpan";
            }
            if (type == typeof(TimeSpan?))
            {
                return "TimeSpan?";
            }

            if (type == typeof(DateOnly))
            {
                return "DateOnly";
            }
            if (type == typeof(DateOnly?))
            {
                return "DateOnly?";
            }

            if (type == typeof(TimeOnly))
            {
                return "TimeOnly";
            }
            if (type == typeof(TimeOnly?))
            {
                return "TimeOnly?";
            }

            return null;
        }

        //public static string GetCSharpTypeName(Type type, bool getFullName)
        //{
        //    StringBuilder sb = new();
        //    if (getFullName && !string.IsNullOrEmpty(type.Namespace))
        //    {
        //        sb.Append(type.Namespace);
        //        sb.Append('.');
        //    }
        //    AppendCSharpTypeName(sb, type, getFullName);
        //    return sb.ToString();
        //}

        //private static void AppendCSharpTypeName(StringBuilder sb, Type type, bool fullParameterNames)
        //{
        //    string typeName = type.Name;
        //    Type declaringType = type.DeclaringType;

        //    int declaringTypeArgumentCount = 0;
        //    if (type.IsNested)
        //    {
        //        if (declaringType.IsGenericTypeDefinition)
        //        {
        //            declaringTypeArgumentCount =
        //                declaringType.GetGenericArguments().Length;
        //            declaringType = declaringType.MakeGenericType(
        //                type.GetGenericArguments().Take(declaringTypeArgumentCount)
        //                    .ToArray());
        //        }

        //        AppendCSharpTypeName(sb, declaringType, fullParameterNames);
        //        sb.Append('.');
        //    }
        //    Type[] genericArguments = type.GetGenericArguments()
        //        .Skip(declaringTypeArgumentCount).ToArray();

        //    int stopIndex;
        //    if ((type.IsGenericTypeDefinition || type.IsGenericType)
        //        && ((stopIndex = type.Name.IndexOf('`')) > 0))
        //    {
        //        sb.Append(typeName.Substring(0, stopIndex));
        //        string[] genericArgumentNames = genericArguments
        //            .Select(t => GetCSharpTypeName(t, fullParameterNames)).ToArray();
        //        if (genericArgumentNames.Length > 0)
        //            sb.AppendFormat("<{0}>", string.Join(",", genericArgumentNames));
        //    }
        //    else
        //    {
        //        sb.Append(typeName);
        //    }
        //}

        #endregion

        #region private

        private readonly string _projectName;

        private string _savePath;

        private string _buildPath;

        private readonly Dictionary<FileInfo, string> fileDict = [];

        private readonly Dictionary<DirectoryInfo, string> folderDict = [];

        private int fileIndex = 0;

        private int folderIndex = -1;

        private Dictionary<string, FastModule> moduleDict;

        private readonly HashSet<string> ignoreFiles = [];

        private readonly HashSet<string> ignoreFolders = [];

        private readonly HashSet<string> ignoreExtensions = [];

        private void AddClassByFolder(DirectoryInfo dirInfo, string baseName)
        {
            var infoList = dirInfo.GetFiles();
            foreach (var fileInfo in infoList)
            {
                //文件拓展
                if (ignoreExtensions.Contains(Path.GetExtension(fileInfo.FullName)))
                {
                    continue;
                }

                if (ignoreFiles.Contains(fileInfo.FullName))
                {
                    continue;
                }

                if (OnFilterFile != null)
                {
                    if (!OnFilterFile(fileInfo))
                    {
                        continue;
                    }
                }
                fileDict.Add(fileInfo, baseName);
            }

            var dirs = dirInfo.GetDirectories();
            foreach (var folder in dirs)
            {
                if (ignoreFolders.Contains(folder.FullName))
                {
                    continue;
                }

                if (OnFilterDirectory != null)
                {
                    if (!OnFilterDirectory(folder))
                    {
                        continue;
                    }
                }
                AddClassByFolder(folder, Path.Combine(baseName, folder.Name));
            }
        }

        private long _time;
        private void SetStartTime()
        {
            _time = Stopwatch.GetTimestamp();
        }

        private void PrintTime()
        {
#if !NET6_0
            var ms = Stopwatch.GetElapsedTime(_time).TotalMilliseconds;
#else
            var ms = TimeSpan.FromMilliseconds(Stopwatch.GetTimestamp() - _time).TotalMilliseconds;
#endif
            OnMessage?.Invoke($"=====>{ms}ms");
        }

        private async Task CopyClassFileAsync()
        {
            await Parallel.ForEachAsync(fileDict, async (kv, token) =>
            {
                var lastTime = File.GetLastWriteTime(kv.Key.FullName);
                var saveDir = Path.Combine(_buildPath, kv.Value);
                var fileInfo = new FileInfo(Path.Combine(saveDir, kv.Key.Name));

                if (fileInfo.Exists)
                {
                    var lastTime2 = File.GetLastWriteTime(fileInfo.FullName);
                    if (lastTime == lastTime2) //最后写入时间相等则不处理
                    {
                        return;
                    }
                    //文件已存在删除
                    File.Delete(fileInfo.FullName);
                }
                else
                {
                    if (!Directory.Exists(saveDir))
                    {
                        Directory.CreateDirectory(saveDir);
                    }
                }

                using (var tarFs = kv.Key.OpenRead())
                using (var tarReader = new StreamReader(tarFs, Encoding.UTF8))
                using (var desFs = fileInfo.OpenWrite())
                using (var desWriter = new StreamWriter(desFs, Encoding.UTF8))
                {
                    string line;
                    while ((line = await tarReader.ReadLineAsync()) != null)
                    {
                        if (UsingPrefix.Count > 0)
                        {
                            if (line.StartsWith("using"))
                            {
                                var arr = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
                                if (UsingPrefix.Any(a => arr[1].StartsWith(a)))
                                {
                                    line = $"using {_projectName}.{arr[1]}";
                                }
                            }
                        }

                        if (line.StartsWith("namespace"))
                        {
                            var arr = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
                            line = $"namespace {_projectName}.{arr[1]}";
                        }

                        if (OnLine != null)
                        {
                            line = OnLine(line, kv.Key);
                        }

                        await desWriter.WriteLineAsync(line);
                    }
                }
                File.SetLastWriteTime(fileInfo.FullName, lastTime);
            });
        }

        private string GetCSharpTypeName(Type type, bool getFullName)
        {
            StringBuilder sb = new();
            if (getFullName && !string.IsNullOrEmpty(type.Namespace))
            {
                if (UsingPrefix.Any(type.Namespace.StartsWith))
                {
                    sb.Append($"{_projectName}.{type.Namespace}");
                }
                else
                {
                    sb.Append(type.Namespace);
                }
                sb.Append('.');
            }
            AppendCSharpTypeName(sb, type, getFullName);
            return sb.ToString();
        }

        private void AppendCSharpTypeName(StringBuilder sb, Type type, bool fullParameterNames)
        {
            string typeName = type.Name;
            Type declaringType = type.DeclaringType;

            int declaringTypeArgumentCount = 0;
            if (type.IsNested)
            {
                if (declaringType.IsGenericTypeDefinition)
                {
                    declaringTypeArgumentCount =
                        declaringType.GetGenericArguments().Length;
                    declaringType = declaringType.MakeGenericType(
                        type.GetGenericArguments().Take(declaringTypeArgumentCount)
                            .ToArray());
                }

                AppendCSharpTypeName(sb, declaringType, fullParameterNames);
                sb.Append('.');
            }
            Type[] genericArguments = type.GetGenericArguments()
                .Skip(declaringTypeArgumentCount).ToArray();

            int stopIndex;
            if ((type.IsGenericTypeDefinition || type.IsGenericType)
                && ((stopIndex = type.Name.IndexOf('`')) > 0))
            {
                sb.Append(typeName.Substring(0, stopIndex));
                string[] genericArgumentNames = genericArguments
                    .Select(t => GetCSharpTypeName(t, fullParameterNames)).ToArray();
                if (genericArgumentNames.Length > 0)
                    sb.AppendFormat("<{0}>", string.Join(",", genericArgumentNames));
            }
            else
            {
                sb.Append(typeName);
            }
        }

        private string CreateReturnName(FastAction action)
        {
            var download = action.AttributeList.Any(a => a is FastDownloadAttribute);

            //下载文件
            if (download)
            {
                return "Task<(Stream stream, string name, long? length)>";
            }

            //用户自定义
            if (action.ActionType == FastActionType.Customer)
            {
                return "Task<Stream>";
            }

            //异步流 IAsyncEnumerable<?>
            if (action.IsJsonStream)
            {
                return GetCSharpTypeName(action.ReturnType, true);
            }

            //重写swagger响应
            var reqResAttr = (FastReqResAttribute)action.AttributeList.FirstOrDefault(f => f is FastReqResAttribute);
            if (reqResAttr != null && reqResAttr.ResType != null)
            {
                var simpleName = GetSimpleTypeName(reqResAttr.ResType);
                if (simpleName != null)
                {
                    return $"async Task<{simpleName}>";
                }
                return $"async Task<{GetCSharpTypeName(reqResAttr.ResType, true)}>";
            }

            Type baseType = null;

            if (action.ActionTypeTask == FastActionTypeTask.Default)
            {
                baseType = action.ReturnType;
            }
            else if (action.ActionTypeTask == FastActionTypeTask.TaskT || action.ActionTypeTask == FastActionTypeTask.ValueTaskT)
            {
                baseType = action.ReturnType.GenericTypeArguments[0];
            }

            if (baseType == null || baseType == typeof(void))
            {
                return "async Task";
            }

            if (baseType != typeof(object))
            {
                var simpleName = GetSimpleTypeName(baseType);
                if (simpleName != null)
                {
                    return $"async Task<{simpleName}>";
                }
                return $"async Task<{GetCSharpTypeName(baseType, true)}>";
            }

            return "async Task<JsonNode>";
        }

        private string CreateMethodPar(FastAction action)
        {
            //是否上传文件
            var upload = action.AttributeList.Any(a => a is FastUploadAttribute);
            var parList = action.ParamList.Where(w => !w.IsCustomerType).ToList();
            var isForm = parList.Any(a => a.RequestType == FastParamType.Form) || upload;

            //重写swagger输入
            Type resetType = null;
            var reqResAttr = (FastReqResAttribute)action.AttributeList.FirstOrDefault(f => f is FastReqResAttribute);
            if (reqResAttr != null && reqResAttr.ReqType != null)
            {
                resetType = reqResAttr.ReqType;
            }

            var sb = new StringBuilder();

            if (resetType != null)
            {
                sb.Append($"{GetCSharpTypeName(resetType, true)} _par, ");
            }

            foreach (var item in parList)
            {
                var simpleName = GetSimpleTypeName(item.Type);
                if (simpleName != null)
                {
                    sb.Append($"{simpleName} {item.Name}, ");
                }
                else if (resetType == null) //强类型
                {
                    sb.Append($"{GetCSharpTypeName(item.Type, true)} {item.Name}, ");
                }
            }

            if (isForm)
            {
                sb.Append("FlurlForm flurlForm, ");
            }

            if (action.IsPostStream)
            {
                sb.Append("FlurlStreamContent streamContent, ");
            }

            return sb.ToString();
        }

        private string CreateMethodBody(FastAction action)
        {
            //是否上传文件
            var upload = action.AttributeList.Any(a => a is FastUploadAttribute);
            var download = action.AttributeList.Any(a => a is FastDownloadAttribute);
            var parList = action.ParamList.Where(w => !w.IsCustomerType).ToList();
            var isForm = parList.Any(a => a.RequestType == FastParamType.Form) || upload;

            //重写swagger输入
            Type resetType = null;
            var reqResAttr = (FastReqResAttribute)action.AttributeList.FirstOrDefault(f => f is FastReqResAttribute);
            if (reqResAttr != null && reqResAttr.ReqType != null)
            {
                resetType = reqResAttr.ReqType;
            }

            //POST参数
            var par = "null";
            if (resetType != null)
            {
                par = "_par";
            }
            else if (action.IsPostStream)
            {
                par = "streamContent";
            }
            else if (parList.Count > 0 && action.HttpMethod == "POST" && !isForm)
            {
                foreach (var item in parList)
                {
                    if (item.RequestType == FastParamType.Body)
                    {
                        par = item.Name;
                        break;
                    }
                }
            }

            var sb = new StringBuilder();

            //Get参数和Form参数
            foreach (var item in parList)
            {
                if (item.RequestType == FastParamType.Query)
                {
                    if (item.IsSimpleType)
                    {
                        if (item.Type == typeof(DateTime) || item.Type == typeof(DateTimeOffset))
                        {
                            sb.AppendLine($$"""
                            _request.SetQueryParam("{{item.Name}}", {{item.Name}}.ToString(Api.TimeFormat));
                            """);
                        }
                        else if (item.Type == typeof(DateTime?) || item.Type == typeof(DateTimeOffset?))
                        {
                            sb.AppendLine($$"""
                            if({{item.Name}}.HasValue)
                            {
                                _request.SetQueryParam("{{item.Name}}", {{item.Name}}.Value.ToString(Api.TimeFormat));
                            }
                            """);
                        }
                        else
                        {
                            sb.AppendLine($$"""
                            _request.SetQueryParam("{{item.Name}}", {{item.Name}});
                            """);
                        }

                    }
                    else
                    {
                        sb.AppendLine($$"""
                            Api.InitGetPar(_request, {{item.Name}});
                            """);
                    }
                }

                if (item.RequestType == FastParamType.Form)
                {
                    if (item.IsSimpleType)
                    {
                        if (item.Type == typeof(string))
                        {
                            sb.AppendLine($$"""
                                flurlForm.AddString("{{item.Name}}", {{item.Name}});
                                """);
                        }
                        else
                        {
                            if (item.Type == typeof(DateTime) || item.Type == typeof(DateTimeOffset))
                            {
                                sb.AppendLine($$"""
                                flurlForm.AddString("{{item.Name}}", {{item.Name}}.ToString(Api.TimeFormat));
                                """);
                            }
                            else if (item.Type == typeof(DateTime?) || item.Type == typeof(DateTimeOffset?))
                            {
                                sb.AppendLine($$"""
                                if({{item.Name}}.HasValue)
                                {
                                    flurlForm.AddString("{{item.Name}}", {{item.Name}}.Value.ToString(Api.TimeFormat));
                                }
                                """);
                            }
                            else
                            {
                                sb.AppendLine($$"""
                                flurlForm.AddString("{{item.Name}}", {{item.Name}}.ToString());
                                """);
                            }
                        }
                    }
                    else
                    {
                        sb.AppendLine($$"""
                            Api.InitFormPar(flurlForm, {{item.Name}});
                            """);
                    }
                }

            }

            //下载文件
            if (download)
            {
                //返回Task<(Stream stream, string name, long? length)>
                if (action.HttpMethod == "GET")
                {
                    sb.AppendLine($$"""
                        return Api.GetDownloadStreamAsync(_request, {{par}}, cancellationToken);
                        """);
                }
                else if (isForm)
                {
                    sb.AppendLine($$"""
                        return Api.FormDownloadStreamAsync(_request, {{par}}, flurlForm, cancellationToken);
                        """);
                }
                else
                {
                    sb.AppendLine($$"""
                        return Api.PostDownloadStreamAsync(_request, {{par}}, cancellationToken);
                        """);
                }

                return sb.ToString();
            }

            //用户自定义
            if (action.ActionType == FastActionType.Customer)
            {
                //返回Task<Stream>
                if (action.HttpMethod == "GET")
                {
                    sb.AppendLine($$"""
                        return Api.GetStreamAsync(_request, {{par}}, cancellationToken);
                        """);
                }
                else if (isForm)
                {
                    sb.AppendLine($$"""
                        return Api.FormGetStreamAsync(_request, {{par}}, flurlForm, cancellationToken);
                        """);
                }
                else
                {
                    sb.AppendLine($$"""
                        return Api.PostGetStreamAsync(_request, {{par}}, cancellationToken);
                        """);
                }

                return sb.ToString();
            }

            //异步流 IAsyncEnumerable<?>
            if (action.IsJsonStream)
            {
                var t = action.ReturnType.GenericTypeArguments[0];
                var tName = GetCSharpTypeName(t, true);
                if (action.HttpMethod == "GET")
                {
                    sb.AppendLine($$"""
                        return Api.GetJsonStreamAsync<{{tName}}>(_request, {{par}}, cancellationToken);
                        """);
                }
                else if (isForm)
                {
                    sb.AppendLine($$"""
                        return Api.FormGetJsonStreamAsync<{{tName}}>(_request, {{par}}, flurlForm, cancellationToken);
                        """);
                }
                else
                {
                    sb.AppendLine($$"""
                        return Api.PostGetJsonStreamAsync<{{tName}}>(_request, {{par}}, cancellationToken);
                        """);
                }

                return sb.ToString();
            }

            Type baseType = null;

            if (action.ActionTypeTask == FastActionTypeTask.Default)
            {
                baseType = action.ReturnType;
            }
            else if (action.ActionTypeTask == FastActionTypeTask.TaskT || action.ActionTypeTask == FastActionTypeTask.ValueTaskT)
            {
                baseType = action.ReturnType.GenericTypeArguments[0];
            }

            if (baseType == null || baseType == typeof(void))
            {
                //返回async Task
                if (action.HttpMethod == "GET")
                {
                    sb.AppendLine($$"""
                        var _response = await Api.GetJsonAsync<JsonElement>(_request, {{par}}, cancellationToken);
                        GetJsonData<JsonNode>(_response, _request);
                        """);
                }
                else if (isForm)
                {
                    sb.AppendLine($$"""
                        var _response = await Api.FormGetJsonAsync<JsonElement>(_request, {{par}}, flurlForm, cancellationToken);
                        GetJsonData<JsonNode>(_response, _request);
                        """);
                }
                else
                {
                    sb.AppendLine($$"""
                        var _response = await Api.PostGetJsonAsync<JsonElement>(_request, {{par}}, cancellationToken);
                        GetJsonData<JsonNode>(_response, _request);
                        """);
                }

                return sb.ToString();
            }

            if (baseType != typeof(object))
            {
                var simpleName = GetSimpleTypeName(baseType);
                if (simpleName == null)
                {
                    simpleName = GetCSharpTypeName(baseType, true);
                }
                //返回 async Task<?>
                if (action.HttpMethod == "GET")
                {
                    sb.AppendLine($$"""
                        var _response = await Api.GetJsonAsync<JsonElement>(_request, {{par}}, cancellationToken);
                        return GetJsonData<{{simpleName}}>(_response, _request);
                        """);
                }
                else if (isForm)
                {
                    sb.AppendLine($$"""
                        var _response = await Api.FormGetJsonAsync<JsonElement>(_request, {{par}}, flurlForm, cancellationToken);
                        return GetJsonData<{{simpleName}}>(_response, _request);
                        """);
                }
                else
                {
                    sb.AppendLine($$"""
                        var _response = await Api.PostGetJsonAsync<JsonElement>(_request, {{par}}, cancellationToken);
                        return GetJsonData<{{simpleName}}>(_response, _request);
                        """);
                }

                return sb.ToString();
            }

            //返回async Task<JsonNode>
            if (action.HttpMethod == "GET")
            {
                sb.AppendLine($$"""
                        var _response = await Api.GetJsonAsync<JsonElement>(_request, {{par}}, cancellationToken);
                        return GetJsonData<JsonNode>(_response, _request);
                        """);
            }
            else if (isForm)
            {
                sb.AppendLine($$"""
                        var _response = await Api.FormGetJsonAsync<JsonElement>(_request, {{par}}, flurlForm, cancellationToken);
                        return GetJsonData<JsonNode>(_response, _request);
                        """);
            }
            else
            {
                sb.AppendLine($$"""
                        var _response = await Api.PostGetJsonAsync<JsonElement>(_request, {{par}}, cancellationToken);
                        return GetJsonData<JsonNode>(_response, _request);
                        """);
            }

            return sb.ToString();
        }

        private StreamWriter _writer;

        private async Task WriteLineAsync()
        {
            await _writer.WriteLineAsync();
        }

        private async Task WriteLineAsync(string line, int space)
        {
            await _writer.WriteLineAsync(new string(' ', space * 4) + line);
        }

        private async Task GenSdkFileAsync()
        {
            var fullName = Path.Combine(_buildPath, $"{DefaultSdkClassName}.cs");
            if (File.Exists(fullName))
            {
                File.Delete(fullName);
            }

            using var fs = new FileStream(fullName, FileMode.CreateNew);
            using var writer = new StreamWriter(fs, Encoding.UTF8);
            _writer = writer;

            #region write using

            foreach (var us in Usings)
            {
                await writer.WriteLineAsync($"using {us};");
            }

            #endregion

            #region namespace start

            await writer.WriteLineAsync();
            await writer.WriteLineAsync($"namespace {_projectName}");
            await writer.WriteLineAsync('{');

            #endregion

            #region class start

            await WriteLineAsync($"public partial class {DefaultSdkClassName}", 1);
            await WriteLineAsync("{", 1);

            #endregion

            #region class struct

            await WriteLineAsync($"public {DefaultSdkClassName}(string url)", 2);
            await WriteLineAsync("{", 2);
            await WriteLineAsync("Api = new FlurlApi(url);", 3);
            await WriteLineAsync("}", 2);

            #endregion

            #region common

            await WriteLineAsync();
            await WriteLineAsync("#region common", 2);

            await WriteLineAsync();
            await WriteLineAsync("public FlurlApi Api { get; }", 2);

            await WriteLineAsync();
            if (string.IsNullOrEmpty(DefaultSdkGetJsonDataCode))
            {
                DefaultSdkGetJsonDataCode = """
                    var code = el.GetProperty("code").GetInt32();
                                if (code < 0)
                                {
                                    var msg = el.GetProperty("msg").GetString();
                                    throw new Exception($"{req?.Url}==>{msg}");
                                }
                                var data = el.GetProperty("data");
                                if (data.ValueKind == JsonValueKind.Null)
                                {
                                    return default;
                                }
                                return data.Deserialize<T>(FlurlExt.JsonOptions);
                    """;
            }

            var getDataStatic = "";
            if (DefaultSdkGetJsonDataStatic)
            {
                getDataStatic = " static";
            }

            await writer.WriteLineAsync($$"""
                        public{{getDataStatic}} T GetJsonData<T>(JsonElement el, IFlurlRequest req)
                        {
                            {{DefaultSdkGetJsonDataCode}}
                        }
                """);

            await WriteLineAsync();
            await WriteLineAsync("#endregion", 2);

            #endregion

            #region api

            await WriteLineAsync();
            await WriteLineAsync("#region api", 2);

            var ti = CultureInfo.CurrentCulture.TextInfo;
            foreach (var keyVal in moduleDict)
            {
                var baseRoute = keyVal.Value.RouteName;
                baseRoute = baseRoute.Replace('-', '/');
                baseRoute = baseRoute.Replace('_', '/');
                var array = baseRoute.Split("/", StringSplitOptions.RemoveEmptyEntries);
                for (int i = 0; i < array.Length; i++)
                {
                    array[i] = ti.ToTitleCase(array[i]);
                }
                var prefix = string.Join("_", array);

                foreach (var keyVal2 in keyVal.Value.ActionDict)
                {
                    var action = keyVal2.Value;
                    if (action.ActionType == FastActionType.View || action.ActionType == FastActionType.Redirect)
                    {
                        continue;
                    }

                    var routeName = keyVal.Value.RouteName + action.RouteName; //路由名称
                    var methodName = prefix + "_" + action.MethodName; //方法名称
                    await WriteLineAsync();
                    await WriteLineAsync();
                    await writer.WriteLineAsync($$"""
                        public {{CreateReturnName(action)}} {{methodName}}({{CreateMethodPar(action)}}CancellationToken cancellationToken = default)
                        {
                            var _request = Api.CreateRequest("{{routeName}}");
                            {{CreateMethodBody(action)}}
                        }
                        """);
                }
            }

            await WriteLineAsync();
            await WriteLineAsync("#endregion", 2);

            #endregion

            #region class end

            await WriteLineAsync("}", 1);

            #endregion

            #region namespace end

            await writer.WriteLineAsync('}');

            #endregion

            _writer = null;
        }

        #endregion

        public HashSet<string> Packages { get; } = [];

        public HashSet<string> UsingPrefix { get; } = [];

        public HashSet<string> Usings { get; } = [];

        public Func<FileInfo, bool> OnFilterFile;

        public Func<DirectoryInfo, bool> OnFilterDirectory;

        public Func<string, FileInfo, string> OnLine;

        public event Action<string> OnMessage;

        public string DefaultClassFolder { get; set; } = "_Class";

        public string DefaultFolder { get; set; } = "_Folder";

        public string DefaultSdkClassName { get; set; } = "ApiClient";

        public bool DefaultSdkGetJsonDataStatic { get; set; } = true;

        public string DefaultSdkGetJsonDataCode { get; set; }

        public FastClientSdk(string name)
        {
            _projectName = name;
            Packages.Add("OkFlurl");

            Usings.Add("Flurl.Http");
            Usings.Add("OkFlurl");
            Usings.Add("System.Text.Json");
            Usings.Add("System.Text.Json.Nodes");

            ignoreExtensions.Add(".csproj");
        }

        public string NetVersion { get; set; }

        public void AddIgnoreFile(string fullPath)
        {
            var info = new FileInfo(fullPath);
            ignoreFiles.Add(info.FullName);
        }

        public void AddIgnoreFolder(string folder)
        {
            var info = new DirectoryInfo(folder);
            ignoreFolders.Add(info.FullName);
        }

        public void AddIgnoreExtension(string extension)
        {
            ignoreExtensions.Add(extension);
        }

        /// <summary>
        /// 添加文件
        /// </summary>
        /// <param name="fileName"></param>
        public void AddClassFile(string fullPath)
        {
            //文件拓展
            if (ignoreExtensions.Contains(Path.GetExtension(fullPath)))
            {
                return;
            }

            var fileInfo = new FileInfo(fullPath);

            //忽略文件
            if (ignoreFiles.Contains(fileInfo.FullName))
            {
                return;
            }

            //文件过滤器
            if (OnFilterFile != null)
            {
                if (!OnFilterFile(fileInfo))
                {
                    return;
                }
            }

            //已存在返回
            if (fileDict.Any(a => a.Key.FullName == fileInfo.FullName))
            {
                return;
            }

            var folderName = DefaultClassFolder;

            //已存在这个文件名，索引加1
            if (fileDict.Any(a => a.Key.Name == fileInfo.Name))
            {
                fileIndex++;
                if (fileIndex > 0)
                {
                    folderName += fileIndex;
                }
            }
            fileDict.Add(fileInfo, folderName);
        }

        /// <summary>
        /// 添加文件夹
        /// </summary>
        /// <param name="folder"></param>
        public void AddClassFolder(string folder)
        {
            var folderInfo = new DirectoryInfo(folder);

            //忽略文件夹
            if (ignoreFolders.Contains(folderInfo.FullName))
            {
                return;
            }

            //文件夹过滤器
            if (OnFilterDirectory != null)
            {
                if (!OnFilterDirectory(folderInfo))
                {
                    return;
                }
            }

            //已存在返回
            if (folderDict.Any(a => a.Key.FullName == folderInfo.FullName))
            {
                return;
            }

            string folderName = DefaultFolder;
            folderIndex++;
            if (folderIndex > 0)
            {
                folderName += folderIndex;
            }

            folderDict.Add(folderInfo, folderName);
            AddClassByFolder(folderInfo, folderName);
        }

        /// <summary>
        /// 创建项目
        /// </summary>
        /// <param name="projectPath"></param>
        /// <returns></returns>
        public async Task CreateProjectAsync(string savePath, Dictionary<string, FastModule> moduleDict)
        {
            _savePath = savePath;
            this.moduleDict = moduleDict.OrderBy(b => b.Key).ToDictionary(el => el.Key, el => el.Value);

            var saveFolder = Path.Combine(_savePath, _projectName);
            if (!Directory.Exists(saveFolder))
            {
                Directory.CreateDirectory(saveFolder);
            }

            _buildPath = Path.Combine(saveFolder, _projectName);
            if (!Directory.Exists(_buildPath))
            {
                Directory.CreateDirectory(_buildPath);
            }

            string ver = null;
            if (!string.IsNullOrEmpty(NetVersion))
            {
                ver = $" -f {NetVersion}";
            }

            string cmd = $"new classlib -o {_projectName}{ver}";
            OnMessage?.Invoke($"=========={cmd}==========");
            SetStartTime();
            var newLibRes = await RunDotnetCmdAsync(cmd, saveFolder);
            PrintTime();
            OnMessage?.Invoke(newLibRes);

            cmd = $"new sln --name {_projectName}";
            SetStartTime();
            OnMessage?.Invoke($"=========={cmd}==========");
            var newSlnRes = await RunDotnetCmdAsync(cmd, saveFolder);
            PrintTime();
            OnMessage?.Invoke(newSlnRes);

            cmd = $"sln add {_projectName}";
            OnMessage?.Invoke($"=========={cmd}==========");
            SetStartTime();
            var slnAddRes = await RunDotnetCmdAsync(cmd, saveFolder);
            PrintTime();
            OnMessage?.Invoke(slnAddRes);

            var class1 = Path.Combine(_buildPath, "Class1.cs");
            if (File.Exists(class1))
            {
                File.Delete(class1);
                OnMessage?.Invoke("Delete Class1.cs");
            }

            OnMessage?.Invoke($"==========CopyClassFile Start==========");
            SetStartTime();
            await CopyClassFileAsync();
            PrintTime();
            OnMessage?.Invoke($"==========CopyClassFile End==========\n");

            OnMessage?.Invoke($"==========GenSdkFile Start==========");
            SetStartTime();
            await GenSdkFileAsync();
            PrintTime();
            OnMessage?.Invoke($"==========GenSdkFile End==========\n");

            foreach (var package in Packages)
            {
                cmd = $"add package {package}";
                OnMessage?.Invoke($"=========={cmd}==========");
                SetStartTime();
                var addPckRes = await RunDotnetCmdAsync(cmd, _buildPath);
                PrintTime();
                OnMessage?.Invoke(addPckRes);
            }
        }

        public async Task BuildProjectAsync()
        {
            if (string.IsNullOrEmpty(_buildPath))
            {
                throw new Exception("_buildPath is null or empty");
            }
            var cmd = $"build -c Release";
            OnMessage?.Invoke($"=========={cmd}==========");
            SetStartTime();
            var res = await RunDotnetCmdAsync(cmd, _buildPath);
            PrintTime();
            OnMessage?.Invoke(res);
        }

        public async Task PackProjectAsync()
        {
            if (string.IsNullOrEmpty(_buildPath))
            {
                throw new Exception("_buildPath is null or empty");
            }

            var cmd = $"pack";
            OnMessage?.Invoke($"=========={cmd}==========");
            SetStartTime();
            var res = await RunDotnetCmdAsync(cmd, _buildPath);
            PrintTime();
            OnMessage?.Invoke(res);
        }

        public async Task CreateSdkFileAsync(string savePath, Dictionary<string, FastModule> moduleDict)
        {
            _buildPath = savePath;
            this.moduleDict = moduleDict;
            await GenSdkFileAsync();
        }

    }
}
